"""
Copyright 2009, Thomas Dejanovic, Jay Shurtz.
 
This is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.

This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this software; if not, write to the Free
Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA, or see the FSF site: http://www.fsf.org.
"""

id = "$Id: hatch_property_elaborator.py 667 2010-07-01 00:12:17Z jayshurtz $"
# $URL: http://hatch.googlecode.com/svn/trunk/hatch/hatch_visitors/hatch_property_elaborator.py $
# $Author: jayshurtz $
version = " ".join(id.split()[1:3])


import to_hatch_structure
from hatch_visitor import *
from hatch_constants import GLOBAL_NAME#, LOCAL_NAME


class HatchPropertyElaborator(HatchVisitor):
    """ Class for generating additional hatch node properties from a node's existing properties (and type.)
        This is done to flesh out the design.  Any new properties are made available to all target output formats this way.
        This class implements all the property elaboration methods that would otherwise be needed by the hatch node classes themselves.
    """
    def getIllegalNames(self, hatchNode):
        """ Generate a list of 'illegal' global names from each node's .name property.
            This will give a list of 'desired names in contention' before 
            global name allocation begins.
        """
        namesSoFar = set()
        namesInContention = set()
        for node in HatchNodeIteratorBreadthFirst(hatchNode):
            desiredGlobalName = node.properties.get(GLOBAL_NAME, node.name)
            if desiredGlobalName in namesSoFar:
                namesInContention |= set([desiredGlobalName])
            else:
                namesSoFar |= set([desiredGlobalName])
        return namesInContention

    def visit_function(self, hatchNode):
        """ Elaborate hatch node properties & recurse into child nodes to elaborate the entire design.
            This visitor uses breadth-first recursion.
        """
        self.globalNames = self.getIllegalNames(hatchNode)    # Set of globally unique names, cleared with each visit.
        for node in HatchNodeIteratorBreadthFirst(hatchNode):
            self.globalName(node)
            # XXX Add other funcions here.

    def globalName(self, hatchNode):
        """ Generate a globally unique name for each hatch node instance in a design.
            This is necessary when generating python (from python or from xml) as all
            instances share the same scope.  The unique global name property is used as so:

            global_name1 = HatchNode('name', *childNodes, **properties)
            global_name2 = HatchNode('name', *childNodes, **properties)
            etc...

            This function assumes that the property checker has already been run, and
            that all child nodes of each hatch node already have unique .name attributes
            at that local level.
        """
        #logger.info("Setting global name!")
        hatchNodeParent = hatchNode.parent

    #    if hatchNodeParent is None:
    #        global_name = hatchNode.name
    #    else:
    #        global_name = hatchNodeParent.properties.get(GLOBAL_NAME, hatchNodeParent.name) + "_" + hatchNode.name

        global_name = hatchNode.properties.get(GLOBAL_NAME, hatchNode.name) # Get unique name if it already exists.
        if global_name in self.globalNames:   # If name already exists.
            if hatchNodeParent is not None:
                global_name = hatchNodeParent.properties.get(GLOBAL_NAME, hatchNodeParent.name) + "_" + global_name   # Safe as this recurses top-down.

        # If name is still not unique.  This can happen if there is no parent node.
        count = 0
        while global_name in self.globalNames:    # Count forever until unique.
            count += 1
            global_name = global_name + '_' + str(count)

        self.globalNames |= set([global_name])  # Add unique name to set.
        hatchNode.properties[GLOBAL_NAME] = global_name


# Main for test
if __name__ == "__main__":

    consoleHandler.setLevel(logging.DEBUG)
    hpe = HatchPropertyElaborator()

    #       h6
    #      /  \
    #     h5   h4
    #    /  \    \
    #   h1   h2   h3
    #  /  \
    # h7   h0
    from hatch_node_physical import HatchNodePhysical
    h0 = HatchNodePhysical('h0', global_name='h', bit_width=8, stringProp='hey!')
    h7 = HatchNodePhysical('h7', global_name='h')
    h1 = HatchNodePhysical('h1', h0, h7, global_name='h', bit_width=10, stringProp='hey!!')
    h2 = HatchNodePhysical('h2', global_name='h', bit_width=24)
    h3 = HatchNodePhysical('h3', global_name='h', bit_width=32)
    h4 = HatchNodePhysical('h4', h3, global_name='h')
    h5 = HatchNodePhysical('h5', h2, h1, global_name='h')
    h6 = HatchNodePhysical('h6', h5, h4, global_name='h')

    h6.visit(hpe)
    print h6.info()


